import { ThrottleUtilParamFun, ThrottleUtilReturn } from '../types'

export type ThrottleByLimitOptionsType = {
  tag?: string
  limit?: number
  thisArg?: any
}

/**
 * 根据目前处理执行状态的函数个数对函数进行节流处理，当目前处于执行状态的函数个数已达限制，则
 * 不再执行函数，直到有函数调用执行完成使得目前处于执行状态的函数个数降低到限制以下时，才能够
 * 继续调用执行函数。
 *
 * @param {ThrottleUtilParamFun} fun 要进行节流处理的函数
 * @param {ThrottleByLimitOptionsType} options 配置选项
 * - tag: 目前处于执行状态的函数个数标记名称，默认为'_throttle'
 * - limit: 同时处理处于执行状态的函数个数限制，默认为 1
 * - thisArg: 指定需要节流处理的函数(传统函数)调用时的 this 指向，默认为 null，即非严格模
 * 式下函数调用时的 this 指向全局对象，严格模式下为 thisArg 指定的值；对于需要节流处理的函
 * 数为箭头函数时，该参数无效，箭头函数的 this 指向由函数定义时确定，不受 thisArg 参数影响，
 * 箭头函数的 this 来源于箭头函数定义时的上级作用域。
 * @returns {ThrottleUtilReturn} 节流处理后的函数
 */
export const throttleByLimit = (
  fun: ThrottleUtilParamFun,
  options?: ThrottleByLimitOptionsType
): ThrottleUtilReturn => {
  const { tag, limit, thisArg } = {
    tag: '_throttle',
    limit: 1,
    thisArg: null,
    ...options
  }
  // 为函数添加目前处于执行状态的函数个数标记
  Reflect.defineProperty(fun, tag, { value: 0, writable: true })
  return async (...args: any[]): Promise<void | any> => {
    // 如果目前处于执行状态的函数个数已达限制
    if ((Reflect.get(fun, tag) as number) >= limit) return
    // 处于执行状态的函数个数加一
    Reflect.set(fun, tag, (Reflect.get(fun, tag) as number) + 1)
    // 调用函数
    const res = await fun.apply(thisArg, args)
    // 处于执行状态的函数个数减一
    Reflect.set(fun, tag, (Reflect.get(fun, tag) as number) - 1)
    // 返回函数调用结果
    return res
  }
}

export type ThrottleByLimitType = typeof throttleByLimit
